﻿using GACore;
using Model;
using System;
using System.Linq;
using System.Text;
using ME = Model.Environment;

namespace PflanzenZAOptimierung
{
	public class PlantsCAProblem : Problem
	{
		private Interpreter.Controller zaController;
		private VectorRulesConverter vectorRulesConverter;
		private int initialStepCount;
		private int stepCount;
		private string[] rules;

		public PlantsCAProblem(int stepCount, int initialStepCount, string cellDefinitionPath, string environmentDefinitionPath, string ruleDefinitionPath)
		{
			this.stepCount = stepCount;
			this.initialStepCount = initialStepCount;
			this.zaController = new Interpreter.Controller();
			ME.CellDefinitions = zaController.LoadCellDefinitions(cellDefinitionPath).ToDictionary(k => k.Name);
			ME.Preferences = zaController.LoadPreferences(environmentDefinitionPath);
			rules = zaController.LoadRules(ruleDefinitionPath);
			zaController.InterpreteRules(rules);
			InitFieldAllocation();
		}

		private void InitFieldAllocation()
		{
			ME.Cells = new Model.Cell[ME.Preferences.SizeWidth, ME.Preferences.SizeHeight];
			ME.Trajectory.Initialize();
			CreateRandomConfiguration();
		}

		private void CreateRandomConfiguration()
		{
			for (int x = 0; x < ME.Cells.GetLength(0); x++)
			{
				for (int y = 0; y < ME.Cells.GetLength(1); y++)
				{
					double rd = random.NextDouble();
					double sum = 0;
					int index = 0;
					foreach (var kvPair in ME.Preferences.CellDistribution)
					{
						sum += kvPair.Value;
						if (sum >= rd)
						{
							ME.Cells[x, y] = new Cell(kvPair.Key, new System.Drawing.Point(x, y));
							ME.Trajectory.AddToCurrentValue(kvPair.Key);
							break;
						}
						index++;
					}
				}
			}
			ME.Trajectory.PrepareNextStep();
		}

		protected override Vector[] CreateInitialPopulation()
		{
			this.vectorRulesConverter = new VectorRulesConverter(rules, preferences.FitnessProperties, preferences.CrossoverProperties);
			InitZA();
			Vector<double>[] ret = new Vector<double>[preferences.PopulationCount];
			for (int i = 0; i < this.preferences.PopulationCount; i++)
			{
				ret[i] = CreateRandomVector();
			}
			return ret;
		}

		private void InitZA()
		{
			Console.WriteLine("Der ZA muss sich zunächst einpendeln. Dies kann einige Minuten dauern.");
			for (int i = 0; i < initialStepCount; i++)
			{
				zaController.CalculateStep();
				if (i % 20 == 0)
				{
					Console.WriteLine("{0:0%} abgeschlossen...", i / (double)initialStepCount);
				}
			}
			Console.WriteLine("100% abgeschlossen.");
		}

		private Vector<double> CreateRandomVector()
		{
			return vectorRulesConverter.CreateRandomVector(random);
		}

		protected override double ComputeFitnessIndividual(Vector vector)
		{
			InitZAController(vector);
			for (int i = 0; i < stepCount; i++)
			{
				zaController.CalculateStep();
			}
			Vector<double> resultVector = GetResultVector();
			return resultVector.ComputeFitnessEuklidDistance(preferences.FitnessProperties.CompareVector);
		}

		private void InitZAController(Vector vector)
		{
			zaController.InterpreteRules(GetRulesFromVector(vector));
		}

		private string[] GetRulesFromVector(Vector vector)
		{
			Vector<double> currentVector = (Vector<double>)vector;
			return vectorRulesConverter.VectorToRules(ref currentVector);
		}

		private Vector<double> GetResultVector()
		{
			Vector<double> retVector = new Vector<double>(4, null, null);
			retVector[0] = GetMeanValueTrajectory("Buche", 5);
			retVector[1] = GetMeanValueTrajectory("Birke", 5);
			retVector[2] = GetMeanValueTrajectory("Gräser", 5);
			retVector[3] = GetMeanValueTrajectory("Leer", 5);
			return retVector;
		}

		private double GetMeanValueTrajectory(string key, int lastX)
		{
			double sum = 0;
			for (int i = 1; i <= lastX; i++)
			{
				sum += ME.Trajectory[key][ME.Trajectory[key].Count - i];
			}
			return sum / lastX;
		}


		public override string GetProblemName()
		{
			return "Pflanzen-ZA-Optimierung";
		}

		public override string GetProblemRepresentation()
		{
			StringBuilder sb = new StringBuilder();
			for (int i = 0; i < vectors.Length; i++)
			{
				sb.AppendLine("Vektor " + i);
				Vector<double> currentVector = (Vector<double>)vectors[i];
				Array.ForEach(vectorRulesConverter.VectorToRules(ref currentVector), x => sb.AppendLine(x));
				sb.AppendLine();
			}
			return sb.ToString();
		}
	}
}
